#include "StdAfx.h"
#include "SurfaceBezier.h"
#include "afficheurErreurs.h"

SurfaceBezier::SurfaceBezier():_carreau(5)
{
}
SurfaceBezier::SurfaceBezier(int nBPtCtrl):_carreau(nBPtCtrl)
{
}
SurfaceBezier::SurfaceBezier(SurfaceBezier &s):_carreau(s._carreau)
{
}
SurfaceBezier::SurfaceBezier(Tableaux<CourbeBezier> surface):_carreau(surface)
{
}
CourbeBezier & SurfaceBezier::operator [](int ligne)
{
	if(ligne<0 || ligne >= int(_carreau.Nb_Effectifs()))
		throw string("Carreau de bezier : Acces en dehors du tableau");
	return _carreau[ligne];
}
void SurfaceBezier::ajoutCourbe(const CourbeBezier &courbe, int pos=-1)
{
	if(pos<0)
		_carreau.Inserer(courbe,_carreau.Nb_Effectifs());
	else 
		_carreau.Inserer(courbe,pos);
}
Points3D SurfaceBezier::evaluer(double u,double v)
{
	int i,taille = int(_carreau.Nb_Effectifs());
	Tableaux<Points3D> tab(taille);
	for(i=0; i<taille;i++)
		tab[i] = _carreau[i].evaluer(v);
	return CourbeBezier(tab).evaluer(u);
}
Points3D & SurfaceBezier::getPoint(int i, int j)
{
	return _surface[i][j];
}
Tableaux<FacesP> & SurfaceBezier::getSolide()
{
	return _solide;
}
void SurfaceBezier::genererSurface(int largeur,int longeur)
{
	int i,j;
	double u, v;
	_surface = vector<vector<Points3D>>(largeur,longeur);
	for(i = 0; i<_surface.size();i++)
	{
		u = double(i)*(1.0/double(_surface.size()-1));
		for(j=0; j<_surface[i].size();j++)
		{
			v = double(j)*(1.0 /double(_surface[i].size()-1));
			_surface[i][j] = evaluer(u,v);
		}
	}
}
void SurfaceBezier::construireFaceTriangulaire(Points3D &p1,Points3D &p2,Points3D &p3,FacesP &face,const AttributsFacesP *af)
{
	Tableaux<AretesP> aretes(3); 
	SommetsP sommet[3];
	sommet[0] = SommetsP(p1);
	sommet[1] = SommetsP(p2);
	sommet[2] = SommetsP(p3);
	for(int i=0; i<3;i++)
		aretes[i] = AretesP(sommet[i],sommet[(i+1)%3]);
	face = FacesP(ContoursP(aretes),af);
}
void SurfaceBezier::creerFace(int l, int h,AttributsFaces *af)
{
	if((_surface.size()-l)<2 || (_surface[0].size()-h)<2)
			return;
	//ajout des faces
	construireFaceTriangulaire(_surface[l][h],_surface[l+1][h],_surface[l+1][h+1],_solide[_solide.Nb_Effectifs()],af);
	construireFaceTriangulaire(_surface[l+1][h+1],_surface[l][h+1],_surface[l][h],_solide[_solide.Nb_Effectifs()],af);
}
void SurfaceBezier::determinerCas(Englobants3D & englobant, FacesP &face, int &cas, int *sommet)
{
	bool test1 = englobant.Appartient(face.IemeSommet(0).Point3D()),
		test2 = englobant.Appartient(face.IemeSommet(1).Point3D()),
		test3 = englobant.Appartient(face.IemeSommet(2).Point3D());
	if(test1)
	{
		if(!test2 && !test3)
		{
			cas = 1;
			sommet[0] = 0;
		}
		else if(test2 && ! test3)
		{
			cas = 2;
			sommet[0] = 0;
			sommet[1] = 1;
			sommet[2] = 2;//le point qui n'est pas dans l'englobant
		}
		else if(!test2 && test3)
		{
			cas = 2;
			sommet[0] = 0;
			sommet[1] = 2;
			sommet[2] = 1;//le point qui n'est pas dans l'englobant
		}
		else
		{
			cas = 3;
			sommet[0] = 0;
			sommet[1] = 1;
			sommet[2] = 2;
		}
	}
	else if(test2)
	{
		if(!test3)
		{
			cas = 1;
			sommet[0] = 1;
		}
		else
		{
			cas = 2;
			sommet[0] = 1;
			sommet[1] = 2;
			sommet[2] = 0;//le point qui n'est pas dans l'englobant
		}
	}
	else if(test3)
	{
		cas = 1;
		sommet[0] = 2;
	}
	else cas = -1;
}
Points3D SurfaceBezier::milieuSegment3D(AretesP & arete)
{
	Points3D p1(arete.Sommet1().Point3D()),p2(arete.Sommet2().Point3D());
	return Points3D((p1.X()+p2.X())*0.5,(p1.Y()+p2.Y())*0.5,(p1.Z()+p2.Z())*0.5);
}
bool SurfaceBezier::centreTriangle(Tableaux<AretesP> & aretes,Points3D & point)
{
	bool test;
	Points3D p1 = milieuSegment3D(aretes[0]);
	Points3D p2 = milieuSegment3D(aretes[1]);
	Droites3D d1(aretes[1].Sommet2().Point3D(),p1);
	Droites3D d2(aretes[0].Sommet1().Point3D(),p2);
	return d1.Intersection(d2,&point,&test);

}
bool SurfaceBezier::centreTriangle(FacesP & face,Points3D & point)
{
	Tableaux<AretesP> aretes(face.ContourExterieur().TableauAretes());
	return centreTriangle(aretes,point);

}
bool SurfaceBezier::centreFaceDansEnglobant(Englobants3D &englobant,FacesP &face)
{
	Points3D centre;
	if(!centreTriangle(face,centre))
		return false;
	bool test1 = englobant.Appartient(Points3D(face.IemeSommet(0).Point3D())),
		test2 = englobant.Appartient(Points3D(face.IemeSommet(1).Point3D())),
		test3 = englobant.Appartient(Points3D(face.IemeSommet(2).Point3D()));
	return englobant.Appartient(centre) || (test1 ||test2 || test3);
}
void SurfaceBezier::contruireFaceRectangulaire(Points3D *points,FacesP &face,const AttributsFacesP *af)
{
	if(points[0].EstEgal(points[1],0.001) || points[3].EstEgal(points[1],0.001))
		construireFaceTriangulaire(points[0],points[2],points[3],face,af);
	else if(points[0].EstEgal(points[2],0.001)|| points[3].EstEgal(points[2],0.001))
		construireFaceTriangulaire(points[0],points[1],points[3],face,af);
	else
	{
		Tableaux<AretesP> aretes(4);
		SommetsP sommet[4];
		for(int i =0; i<4;i++)
			sommet[i] = SommetsP(points[i]);
		for(int i=0; i<4;i++)
			aretes[i] = AretesP(sommet[i],sommet[(i+1)%4]);
		face = FacesP(ContoursP(aretes),af);
	}
}
void SurfaceBezier::decouperFaceCas2(FacesP & face,SolidesP &solide,Tableaux<FacesP> &sortie,int *sommet)
{
	bool test = false;
	Points3D points[4];
	Tableaux<FacesP> facesSol(solide.TableauFaces());
	Tableaux<AretesP> aretes(face.ContourExterieur().TableauAretes());
	int i,j;
	Englobants3D englobant = solide.Englobant();
	points[3] = face.IemeSommet(sommet[0]).Point3D();
	points[0] = face.IemeSommet(sommet[1]).Point3D();
	Points3D point = face.IemeSommet(sommet[2]).Point3D();
	for(i=2;i<6;i++)
	{
		Plans3D plan(facesSol[i].Plan());
		plan.Intersection(points[0],point,points[1],j);
		if(j>2)
		{
			if(fabs(face.Plan().Distance(points[1]))<0.0001)
			{
				plan.Intersection(points[3],point,points[2],j);
				if(j>2)
					if(fabs(face.Plan().Distance(points[2]))<0.0001)
					{
						contruireFaceRectangulaire(points,sortie[sortie.Nb_Effectifs()],face.Attribut());
						break;
					}
			}
		}
	}
}
void SurfaceBezier::decouperFaceCas1(FacesP & face,SolidesP &solide,Tableaux<FacesP> &sortie,int sommet)
{
	bool test = false;
	Points3D points[3];
	Tableaux<FacesP> facesSol(solide.TableauFaces());
	Tableaux<AretesP> aretes(face.ContourExterieur().TableauAretes());
	int i,j,aretePred =(sommet==0? 2 : sommet-1);
	Englobants3D englobant = solide.Englobant();
	points[0] = face.IemeSommet(sommet).Point3D();
	for(i=0;i<6;i++)
	{
		Plans3D plan(facesSol[i].Plan());
		plan.Intersection(aretes[sommet].Sommet1().Point3D(),aretes[sommet].Sommet2().Point3D(),points[1],j);
		if(j>2)
		{
			if(fabs(face.Plan().Distance(points[1]))<0.0001)
			{
				plan.Intersection(aretes[aretePred].Sommet2().Point3D(),aretes[aretePred].Sommet1().Point3D(),points[2],j);
				if(j>2)
					if(fabs(face.Plan().Distance(points[2]))<0.0001)
					{
						FacesP face1;
						int cas,tab[3];
						construireFaceTriangulaire(points[2],points[0],points[1],face1,face.Attribut());
						determinerCas(englobant,face1,cas,tab);
						if(cas==3)
							sortie.Inserer(face1,sortie.Nb_Effectifs());
						else
						{
							if(cas == 2)
								decouperFaceCas2(face1,solide,sortie,tab);
							else if(cas==1)
								decouperFaceCas1(face1,solide,sortie,tab[0]);
							face1.Detruire();
						}
						break;
					}
			}
		}
	}


}
void SurfaceBezier::decoupage(SolidesP & boite,Tableaux<FacesP> &surface,Tableaux<FacesP> & sortie)
{
	int i,taille = surface.Nb_Effectifs() ;
	Englobants3D englobant = boite.Englobant();
	for(i=taille-1;i>=0;i--)
	{
		int tab[3],cas = -1;
		determinerCas(englobant,surface[i],cas,tab);
		switch(cas)
		{
			case 3:
				sortie.Inserer(surface[i],sortie.Nb_Effectifs());
				break;
			case 1:
				decouperFaceCas1(surface[i],boite,sortie,tab[0]);
				surface[i].Detruire();
				break;
			case 2:
				decouperFaceCas2(surface[i],boite,sortie,tab);
				surface[i].Detruire();
				break;
			case 4:
				surface[i].Detruire();
				break;
		}
		surface.Supprimer(i);
	}
	surface.Detruit();
}

bool SurfaceBezier::SubdivisionFaceTriangulaire(FacesP face,FacesP *tabFaces)
{
	Points3D milieu;
	Tableaux<AretesP> aretes(face.ContourExterieur().TableauAretes());
	
	if(!centreTriangle(aretes,milieu))
		return false;

	Tableaux<AretesP> art1(3),art2(3),art3(3);
	SommetsP s1(face.IemeSommet(0)),
		s2(face.IemeSommet(1)),
		s3(face.IemeSommet(2)),
		s4(milieu);

	art1[0] = AretesP(s4,s1);
	art1[1] = AretesP(s1,s2);
	art1[2] = AretesP(s2,s4);
	
	art2[0] = AretesP(s4,s2);
	art2[1] = AretesP(s2,s3);
	art2[2] = AretesP(s3,s4);
	
	art3[0] = AretesP(s1,s4);
	art3[1] = AretesP(s4,s3);
	art3[2] = AretesP(s3,s1);
	
	tabFaces[0] = FacesP(ContoursP(art1),face.Attribut());
	tabFaces[1] = FacesP(ContoursP(art2),face.Attribut());
	tabFaces[2] = FacesP(ContoursP(art3),face.Attribut());
	face.Detruire();
	return true;
}
void SurfaceBezier::triangulerSurface(AttributsFaces *af)
{
	int nbFaces = (_surface.size()-1)*(_surface[0].size()-1)*2;
	_solide = Tableaux<FacesP>(nbFaces);
	for(int i=0,j;i<_surface.size();i++)
		for(j=0;j<_surface[i].size();j++)
			creerFace(i,j,af);
}
SurfaceBezier::~SurfaceBezier()
{
}

void SurfaceBezier::intersection(Englobants3D &englobant,Tableaux<FacesP> & faces,Tableaux<FacesP> & sortie)
{
	long i,taille(faces.Nb_Effectifs());
	for(i=taille-1 ;i>=0;i--)
	{
		if(!centreFaceDansEnglobant(englobant,faces[i]))
			faces[i].Detruire();
		else
			sortie.Inserer(faces[i],sortie.Nb_Effectifs());
		faces.Supprimer(i);
	}
}